﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Windows.Media.Animation;
using Sanford.Multimedia.Midi;
using System.Diagnostics;
using System.Windows.Threading;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input;
using System.Timers;

namespace WpfHero
{
    /// <summary>
    /// The Wpf Hero Engine
    /// </summary>
    public class WHEngine : DependencyObject
    {
        private readonly Dictionary<string, AudioPlayer> _audioPlayers = new Dictionary<string, AudioPlayer>();
        Sequencer _seq;
        Dispatcher _uiThreadDispatcher;
        public event EventHandler<EventArgs> Initialized;
        private Timer _gameHeartbeat;
        private int _positionIndex;

        private const int GameHeartBeatInterval = 50;
        private TimeSpan Tolerance = TimeSpan.FromMilliseconds(200);
        private const string Extension = "wma";

        #region IsButtonXPressed dep props

        /// <summary>
        /// Gets or sets a value indicating whether button1 is pressed.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if button1 is pressed; otherwise, <c>false</c>.
        /// </value>
        public bool IsButton1Pressed
        {
            get { return (bool)GetValue(IsButton1PressedProperty); }
            set { SetValue(IsButton1PressedProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsButton1Pressed.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsButton1PressedProperty =
            DependencyProperty.Register("IsButton1Pressed", typeof(bool), typeof(WHEngine));

        /// <summary>
        /// Gets or sets a value indicating whether button2 is pressed.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if button2 is pressed; otherwise, <c>false</c>.
        /// </value>
        public bool IsButton2Pressed
        {
            get { return (bool)GetValue(IsButton2PressedProperty); }
            set { SetValue(IsButton2PressedProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsButton2Pressed.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsButton2PressedProperty =
            DependencyProperty.Register("IsButton2Pressed", typeof(bool), typeof(WHEngine));

        /// <summary>
        /// Gets or sets a value indicating whether button3 is pressed.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if button3 is pressed; otherwise, <c>false</c>.
        /// </value>
        public bool IsButton3Pressed
        {
            get { return (bool)GetValue(IsButton3PressedProperty); }
            set { SetValue(IsButton3PressedProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsButton3Pressed.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsButton3PressedProperty =
            DependencyProperty.Register("IsButton3Pressed", typeof(bool), typeof(WHEngine));

        /// <summary>
        /// Gets or sets a value indicating whether button4 is pressed.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if button4 is pressed; otherwise, <c>false</c>.
        /// </value>
        public bool IsButton4Pressed
        {
            get { return (bool)GetValue(IsButton4PressedProperty); }
            set { SetValue(IsButton4PressedProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsButton4Pressed.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsButton4PressedProperty =
            DependencyProperty.Register("IsButton4Pressed", typeof(bool), typeof(WHEngine));

        /// <summary>
        /// Gets or sets a value indicating whether button5 is pressed.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if button5 is pressed; otherwise, <c>false</c>.
        /// </value>
        public bool IsButton5Pressed
        {
            get { return (bool)GetValue(IsButton5PressedProperty); }
            set { SetValue(IsButton5PressedProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsButton5Pressed.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsButton5PressedProperty =
            DependencyProperty.Register("IsButton5Pressed", typeof(bool), typeof(WHEngine));

        /// <summary>
        /// Gets or sets a value indicating whether strum is pressed.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if strum is pressed; otherwise, <c>false</c>.
        /// </value>
        public bool IsStrumKeyPressed
        {
            get { return (bool)GetValue(IsStrumKeyPressedProperty); }
            set { SetValue(IsStrumKeyPressedProperty, value); }
        }

        // Using a DependencyProperty as the backing store for IsStrumKeyPressed.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsStrumKeyPressedProperty =
            DependencyProperty.Register("IsStrumKeyPressed", typeof(bool), typeof(WHEngine));



        #endregion

        #region score related DPs


        public int Score
        {
            get { return (int)GetValue(ScoreProperty); }
            set { SetValue(ScoreProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Score.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ScoreProperty =
            DependencyProperty.Register("Score", typeof(int), typeof(WHEngine), new UIPropertyMetadata(0));

        public int MissedGemCount
        {
            get { return (int)GetValue(MissedGemCountProperty); }
            set { SetValue(MissedGemCountProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MissedGemCount.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MissedGemCountProperty =
            DependencyProperty.Register("MissedGemCount", typeof(int), typeof(WHEngine), new UIPropertyMetadata(0));

        public int Multiplier
        {
            get { return (int) GetValue(MultiplierProperty); }
            set { SetValue(MultiplierProperty, value); }
        }

// Using a DependencyProperty as the backing store for Multiplier.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MultiplierProperty =
            DependencyProperty.Register("Multiplier", typeof (int), typeof (WHEngine), new PropertyMetadata(1));


        public int TotalHits
        {
            get { return (int)GetValue(TotalHitsProperty); }
            set { SetValue(TotalHitsProperty, value); }
        }

        // Using a DependencyProperty as the backing store for TotalHits.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty TotalHitsProperty =
            DependencyProperty.Register("TotalHits", typeof(int), typeof(WHEngine), new UIPropertyMetadata(0));



        public int HitsInARow
        {
            get { return (int)GetValue(HitsInARowProperty); }
            set { SetValue(HitsInARowProperty, value); }
        }

        // Using a DependencyProperty as the backing store for HitsInARow.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty HitsInARowProperty =
            DependencyProperty.Register("HitsInARow", typeof(int), typeof(WHEngine), new UIPropertyMetadata(0));



        public int UnneccessaryStrums
        {
            get { return (int)GetValue(UnneccessaryStrumsProperty); }
            set { SetValue(UnneccessaryStrumsProperty, value); }
        }

        // Using a DependencyProperty as the backing store for UnneccessaryStrums.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty UnneccessaryStrumsProperty =
            DependencyProperty.Register("UnneccessaryStrums", typeof(int), typeof(WHEngine), new UIPropertyMetadata(0));



        #endregion

        public TimeSpan EndOfSong { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="WHEngine"/> class.
        /// </summary>
        public WHEngine()
        {
            Gemline1Gems = new ObservableCollection<GemModel>();
            Gemline2Gems = new ObservableCollection<GemModel>();
            Gemline3Gems = new ObservableCollection<GemModel>();
            Gemline4Gems = new ObservableCollection<GemModel>();
            Gemline5Gems = new ObservableCollection<GemModel>();
            AllGems = new ObservableCollection<GemModel>();

            _uiThreadDispatcher = Dispatcher.CurrentDispatcher;
            _gameHeartbeat = new Timer
                                {
                                    AutoReset = true,
                                    Interval = GameHeartBeatInterval
                                };
            _gameHeartbeat.Elapsed += gameHeartbeat_Elapsed;
        }


        private bool strumDownOwnHeartBeat = false;
        /// <summary>
        /// Handles the Elapsed event of the _gameHeartbeat control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Timers.ElapsedEventArgs"/> instance containing the event data.</param>
        private void gameHeartbeat_Elapsed(object sender, ElapsedEventArgs e)
        {
            //check for missed gems

            _uiThreadDispatcher.Invoke(DispatcherPriority.Background, new Action(() =>
            {
                AudioPlayer player = AudioPlayers.Values.First();
                var currentPosition = player.Position;
                TimeSpan prevPosition = currentPosition - Tolerance;
                int i;

                for (i = _positionIndex; i < AllGems.Count && AllGems[i].StartAt < prevPosition; i++)
                {
                    GemModel gem = AllGems[i];
                    if (gem.Status != GemStatus.Hit && gem.Status != GemStatus.Missed)
                        MissGem(gem);
                }

                for (var j = _positionIndex; j < AllGems.Count && AllGems[j].StartAt < currentPosition; j++)
                {
                    GemModel gem = AllGems[j];
                    if (gem.Status == GemStatus.Coming)
                        gem.Status = GemStatus.Current;
                }

                if (!strumDownOwnHeartBeat && IsStrumKeyPressed) //player has just strummed
                {

                    TimeSpan nextPosition = currentPosition + Tolerance;

                    _positionIndex = i;
                    List<GemModel> notes = new List<GemModel>();

                    //notes to hit
                    for (i = _positionIndex; i < AllGems.Count && AllGems[i].StartAt < nextPosition; i++)
                    {
                        GemModel gem = AllGems[i];
                        if (gem.Status != GemStatus.Hit)
                            if (notes.Count > 0)
                            {
                                if (notes[0].StartAt == gem.StartAt)
                                    notes.Add(gem);
                                else
                                    break;
                            }
                            else
                                notes.Add(gem);
                    }

                    if (VerifyButtons(notes))
                    {
                        foreach (var gem in notes)
                            gem.Status = GemStatus.Hit;

                        HitsInARow += notes.Count;
                        TotalHits += notes.Count;
                        Score += notes.Count * Multiplier;
                        if (TotalHits % 10 == 0)
                            Multiplier++;
                    }
                    else
                    {
                        foreach (var gem in notes)
                            gem.Status = GemStatus.Missed;

                        HitsInARow = 0;
                        if (Multiplier != 1)
                            Multiplier = 1;

                        if (notes.Count == 0)
                            UnneccessaryStrums++;
                    }

                }
                strumDownOwnHeartBeat = IsStrumKeyPressed;
            }));
        }

        /// <summary>
        /// Verifies whether the right note buttons are pressed.
        /// </summary>
        /// <param name="notes">The notes to verify.</param>
        /// <returns>true if the right buttons and only they are pressed</returns>
        private bool VerifyButtons(List<GemModel> notes)
        {
            if (CountNoteButtonsDown() != notes.Count)
                return false;

            if (CountNoteButtonsDown() == 0)
                return false;

            foreach (var gem in notes)
                switch (gem.Button)
                {
                    case 1: if (!IsButton1Pressed) return false; break;
                    case 2: if (!IsButton2Pressed) return false; break;
                    case 3: if (!IsButton3Pressed) return false; break;
                    case 4: if (!IsButton4Pressed) return false; break;
                    case 5: if (!IsButton5Pressed) return false; break;
                }

            return true;
        }

        /// <summary>
        /// Counts the note buttons pressed.
        /// </summary>
        /// <returns>number of not buttons pressed</returns>
        private int CountNoteButtonsDown()
        {
            return  (IsButton1Pressed ? 1 : 0) + 
                    (IsButton2Pressed ? 1 : 0) + 
                    (IsButton3Pressed ? 1 : 0) + 
                    (IsButton4Pressed ? 1 : 0) + 
                    (IsButton5Pressed ? 1 : 0);
        }

        /// <summary>
        /// Performs score, etc changes when a gem is missed.
        /// </summary>
        /// <param name="gem">The missed gem.</param>
        private void MissGem(GemModel gem)
        {
            MissedGemCount++;
            gem.Status = GemStatus.Missed;
            HitsInARow = 0;
            Multiplier = 1;
        }

        /// <summary>
        /// Gets the audio players.
        /// </summary>
        /// <value>The audio players.</value>
        public Dictionary<string, AudioPlayer> AudioPlayers
        {
            get { return _audioPlayers; }
        }

        private int difficulty;

        /// <summary>
        /// Gets or sets the difficulty.
        /// </summary>
        /// <value>The difficulty.</value>
        public int Difficulty
        {
            get { return difficulty; }
            private set 
            {
                if (value < 0 || value > 4)
                    throw new ArgumentOutOfRangeException();

                difficulty = value; 
            }
        }


        /// <summary>
        /// Initializes the engine.
        /// </summary>
        /// <param name="songPath">the directory of the song, in Frets on Fire format.
        /// </param>
        public void Initialize(string songPath, int difficulty)
        {
            if (!Directory.Exists(songPath))
                throw new ArgumentException("Cannot find song: " + songPath);

            AllGems.Clear();
            EndOfSong = new TimeSpan(0);

            ////create and initialize midi sequencer
            //_seq = new Sequencer();
            //_seq.ChannelMessagePlayed += new EventHandler<ChannelMessageEventArgs>(seq_ChannelMessagePlayed);
            //_seq.Sequence = new Sequence(Path.Combine(songPath, "notes.mid"));
            //OnNote += new EventHandler<NoteEventArgs>(WHEngine_OnNote);


            //create a audio player for all ogg-s
            AudioPlayers.Clear();
            foreach (var audioFile in Directory.GetFiles(songPath, "*." + Extension))
            {
                var player = new AudioPlayer(audioFile);
                AudioPlayers.Add(Path.GetFileNameWithoutExtension(audioFile), player);
                player.Prepare();
            }

            //fill gems
            _seq = new Sequencer
                      {
                          Sequence = new Sequence(Utils.GetSongPath(songPath))
                      };
            //TODO load sequence asynchronously to keep the UI moving.
            Difficulty = difficulty;

            for (int i = 1; i < 6; i++)
                _uiThreadDispatcher.Invoke(DispatcherPriority.Send,
                                          new Action(() => { GetGemlineByNumber(i).Clear(); }));

            foreach (var gem in GetGemsByDifficulty(_seq.Sequence, Difficulty))
            {
                ObservableCollection<GemModel> line = GetGemlineByNumber(gem.Button);
                if (line.Count > 0)
                    gem.Previous = line[line.Count - 1];
                //else
                //    gem.Status = GemStatus.Missed;
                _uiThreadDispatcher.Invoke(DispatcherPriority.Send, new Action(() =>
                                                                                  {
                                                                                      line.Add(gem);
                                                                                      AllGems.Add(gem);
                                                                                  }));

                if (gem.EndAt > EndOfSong)
                    EndOfSong = gem.EndAt.Value;
            }

            _uiThreadDispatcher.Invoke(DispatcherPriority.Send, new Action( () =>
                                           {
                                               Score = 0;
                                               MissedGemCount = 0;
                                               HitsInARow = 0;
                                               TotalHits = 0;
                                               Multiplier = 1;

                                               _positionIndex = 0;

                                               IsInitialized = true;
                                           }));

            if (Initialized != null)
                Initialized(this, new EventArgs());
        }

        private bool IsInitialized { get; set; }


        //void seq_ChannelMessagePlayed(object sender, ChannelMessageEventArgs e)
        //{
        //    if (e.Message.GetDifficulty() == Difficulty && e.Message.MidiChannel == 0 && OnNote != null)
        //        OnNote(this, new NoteEventArgs() { On = e.Message.Data2 != 0, Button = e.Message.GetButtonNumber() });
                
        //    if (ChannelMessagePlayed != null)
        //        ChannelMessagePlayed(sender, e);
        //}

        //public event EventHandler<NoteEventArgs> OnNote;

        //public event EventHandler<ChannelMessageEventArgs> ChannelMessagePlayed;

        /// <summary>
        /// Handles the PreviewKeyDown event of the MainWindow control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.KeyEventArgs"/> instance containing the event data.</param>
        void MainWindow_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            switch (e.Key)
            {
                case Key.F1: IsButton1Pressed = true; break;
                case Key.F2: IsButton2Pressed = true; break;
                case Key.F3: IsButton3Pressed = true; break;
                case Key.F4: IsButton4Pressed = true; break;
                case Key.F5: IsButton5Pressed = true; break;
                case Key.RightShift: IsStrumKeyPressed = true; break;
            }
        }

        /// <summary>
        /// Handles the PreviewKeyUp event of the MainWindow control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Input.KeyEventArgs"/> instance containing the event data.</param>
        void MainWindow_PreviewKeyUp(object sender, KeyEventArgs e)
        {
            switch (e.Key)
            {
                case Key.F1: IsButton1Pressed = false; break;
                case Key.F2: IsButton2Pressed = false; break;
                case Key.F3: IsButton3Pressed = false; break;
                case Key.F4: IsButton4Pressed = false; break;
                case Key.F5: IsButton5Pressed = false; break;
                case Key.RightShift: IsStrumKeyPressed = false; break;
            }
        }

        private int playingPlayers;

        /// <summary>
        /// Starts a song.
        /// </summary>
        public void Start()
        {
            if (!IsInitialized)
                throw new InvalidOperationException("Please initialize the game engine before calling 'Start'");

            _uiThreadDispatcher.Invoke(DispatcherPriority.Send, new Action(() =>
                              {
                                  _positionIndex = 0;
                                  foreach (var player in AudioPlayers.Values)
                                      player.Start();

                                  Application.Current.MainWindow.PreviewKeyDown += MainWindow_PreviewKeyDown;
                                  Application.Current.MainWindow.PreviewKeyUp += MainWindow_PreviewKeyUp;
                                  _gameHeartbeat.Start();
                              }));

            playingPlayers = _audioPlayers.Count;

            foreach (var player in _audioPlayers.Values)
            {
                player.MediaEnded += PlayerMediaEnded;
            }
        }

        public event EventHandler SongFinished;

        private void PlayerMediaEnded(object sender, EventArgs e)
        {
            playingPlayers--;
            if (playingPlayers < 1 && SongFinished != null)
                SongFinished(this, new EventArgs());

        }

        /// <summary>
        /// Stops the song
        /// </summary>
        public void Stop()
        {
            if (IsInitialized)
            {
                foreach (var player in AudioPlayers.Values)
                {
                    player.Stop();
                    player.MediaEnded -= PlayerMediaEnded;
                }

                _seq.Stop();

                Application.Current.MainWindow.PreviewKeyDown -= MainWindow_PreviewKeyDown;
                Application.Current.MainWindow.PreviewKeyDown -= MainWindow_PreviewKeyUp;

                _gameHeartbeat.Stop();

                IsInitialized = false;
            }
        }

        #region Gem handling

//        System.Timers.Timer gemCleanupTimer = new System.Timers.Timer(1000);

        //void gemCleanupTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        //{
        //    //cleanup gems that have been passed
        //    foreach (var gems in EnumerateGemlines())
        //        while  (gems.Count > 0 && 
        //                gems[0].FinishedAt.HasValue && 
        //                DateTime.Now - gems[0].FinishedAt > TimeSpan.FromSeconds(6))
        //            _uiThreadDispatcher.Invoke(DispatcherPriority.Normal, new Action(() => 
        //                gems.RemoveAt(0)));
        //}


        //private static bool IsMsgNeeded(IMidiMessage msg, int difficulty)
        //{
        //    if (msg is ChannelMessage)
        //        return ((ChannelMessage)msg).GetDifficulty() == difficulty;

        //    return msg is MetaMessage && ((MetaMessage)msg).MetaType == MetaType.Tempo;
        //}

        public static List<GemModel> GetGemsByDifficulty(Sequence seq, int difficulty)
        {
            long tempo = 500000; //default midi tempo, half sec per quarter note

            var currentTimes = new TimeSpan[seq.Count];
            for (var i = 0; i < currentTimes.Length; i++)
                currentTimes[i] = new TimeSpan(0);

            var ticksperqn = seq.Division;

            var allEvents = (from evt in seq[0].Iterator()
                             //where IsMsgNeeded(evt.MidiMessage, difficulty)
                             select new { Evt = evt, Track = 0 }).ToList();

            for (var j = 1; j < seq.Count; j++)
                allEvents.AddRange(from evt in seq[j].Iterator()
                                   //where IsMsgNeeded(evt.MidiMessage, difficulty)
                                   select new { Evt = evt, Track = j });

            var orderedEvents = allEvents.OrderBy(ev => ev.Evt.AbsoluteTicks);

            var result = new List<GemModel>();
            var timeChangeNeeded = new bool[seq.Count];
            //long lastTimeChangeAbsoluteTick = 0;

            foreach (var evt in orderedEvents)
            {
                long delta = evt.Evt.DeltaTicks;
                if (timeChangeNeeded[evt.Track])
                {
                    //delta = evt.Evt.AbsoluteTicks - lastTimeChangeAbsoluteTick;
                    timeChangeNeeded[evt.Track] = false;
                }

                currentTimes[evt.Track] += TimeSpan.FromMilliseconds((double)(delta * tempo) / (double)ticksperqn / 1000.0);
                //                Debug.Write(currentTimes[evt.Track] + " " + evt.Evt.MidiMessage.MessageType.ToString());

                if (evt.Evt.MidiMessage is MetaMessage)
                {
                    var metaMsg = (MetaMessage)(evt.Evt.MidiMessage);
                    if (metaMsg.MetaType == MetaType.Tempo)
                    {
                        //set current time for all tracks to the current
                        var ct = currentTimes[evt.Track];
                        for (var i = 0; i < currentTimes.Length; i++)
                        {
                            currentTimes[evt.Track] = ct;
                            timeChangeNeeded[i] = true;
                        }
                        //lastTimeChangeAbsoluteTick = evt.Evt.AbsoluteTicks;
                        tempo = new TempoChangeBuilder(metaMsg).Tempo;
                        //Debug.WriteLine(" - tempo change: " + tempo);
                    }
                    //if (metaMsg.MetaType == MetaType.Text)
                    //    Debug.WriteLine(String.Format("Text msg on track {0} : ", evt.Track, new MetaTextBuilder(metaMsg).Text));
                }
                //Hack - only accept note msgs on track 1
                else if (evt.Evt.MidiMessage is ChannelMessage && evt.Track == 1)
                {
                    var chmsg = (ChannelMessage)(evt.Evt.MidiMessage);
                    if (chmsg.GetDifficulty() == difficulty)
                    {
                        var lastGem = result.LastOrDefault(gem => gem.Button == chmsg.GetButtonNumber());
                        if (lastGem != null && lastGem.EndAt == null)
                            lastGem.EndAt = currentTimes[evt.Track];

                        //Debug.WriteLine(" - channel msg: difficulty: " + chmsg.GetDifficulty() + " button: " + chmsg.GetButtonNumber() + " - " + chmsg.Data2);
                        if (chmsg.Data2 != 0 && chmsg.Command != ChannelCommand.NoteOff)
                            result.Add(new GemModel() { StartAt = currentTimes[evt.Track], Button = chmsg.GetButtonNumber() });
                        else
                            result[result.Count - 1].EndAt = currentTimes[evt.Track];
                    }
                }
            }

            return result;
        }




        //private IEnumerable<ObservableCollection<GemModel>> EnumerateGemlines()
        //{
        //    yield return Gemline1Gems;
        //    yield return Gemline2Gems;
        //    yield return Gemline3Gems;
        //    yield return Gemline4Gems;
        //    yield return Gemline5Gems;
        //}

        /// <summary>
        /// Gets or sets the gemline1 gems.
        /// </summary>
        /// <value>The gemline1 gems.</value>
        public ObservableCollection<GemModel> Gemline1Gems { get; set; }

        /// <summary>
        /// Gets or sets the gemline2 gems.
        /// </summary>
        /// <value>The gemline2 gems.</value>
        public ObservableCollection<GemModel> Gemline2Gems { get; set; }

        /// <summary>
        /// Gets or sets the gemline3 gems.
        /// </summary>
        /// <value>The gemline3 gems.</value>
        public ObservableCollection<GemModel> Gemline3Gems { get; set; }

        /// <summary>
        /// Gets or sets the gemline4 gems.
        /// </summary>
        /// <value>The gemline4 gems.</value>
        public ObservableCollection<GemModel> Gemline4Gems { get; set; }

        /// <summary>
        /// Gets or sets the gemline5 gems.
        /// </summary>
        /// <value>The gemline5 gems.</value>
        public ObservableCollection<GemModel> Gemline5Gems { get; set; }

        /// <summary>
        /// Gets or sets all gems.
        /// </summary>
        /// <value>All gems.</value>
        public ObservableCollection<GemModel> AllGems { get; set; }

        //void WHEngine_OnNote(object sender, NoteEventArgs e)
        //{
        //    _uiThreadDispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
        //        {
        //            ObservableCollection<GemModel> gemLine = GetGemlineByNumber(e.Button);
        //            if (e.On)
        //                gemLine.Add(new GemModel());
        //            else
        //                gemLine.Last().EndNow();
        //        }));
        //}

        /// <summary>
        /// Gets the gemline by number.
        /// </summary>
        /// <param name="button">The button.</param>
        /// <returns></returns>
        public ObservableCollection<GemModel> GetGemlineByNumber(int button)
        {
            switch (button)
            {
                case 1: return Gemline1Gems;
                case 2: return Gemline2Gems;
                case 3: return Gemline3Gems;
                case 4: return Gemline4Gems;
                case 5: return Gemline5Gems; 
            }

            return null;
        }

        #endregion
    }

    //public class NoteEventArgs : EventArgs
    //{
    //    public bool On { get; set; }
    //    public int Button { get; set; }
    //}

}

